import { Callout } from 'nextra/components'
import BadgeGroup, { UniverTypes } from '@/components/BadgeGroup'

# General API

<BadgeGroup values={[UniverTypes.GENERAL]} value={UniverTypes.GENERAL} />

In Univer, the Facade API that can be called varies according to the different types of documents. This section will introduce the general Facade API that applies to all types of documents.

## Concepts

### Commands

The majority of operations in Univer are registered with the command system, and are triggered through the command system. This unified approach to operations enables Univer to readily implement features such as undo, redo, and collaboration, etc.

<Callout type="info" emoji="ℹ️">
  Commands can be simply understood as unique "events" within Univer. For more details on the design, please refer to [command-system](/guides/sheet/architecture/univer#command-system).
</Callout>

## Commands API

Univer provides users with a unified command system, through which users can implement a variety of customized functionalities.

<Callout type="info" emoji="ℹ️">
  Each command corresponds to a unique ID. If you are looking for a specific command ID, you can refer to [How to find the command ID](/guides/sheet/tutorials/find-the-command-id).
</Callout>

### Listening Commands

Univer provides two ways to listen for commands, before the command is executed and after the command is executed:

- `onBeforeCommandExecute`: Executes custom logic before the command is executed.
- `onCommandExecuted`: Executes custom logic after the command is executed.

#### Listening Commands Before Execution

Before the command is executed, we pass a callback function to the `FUniver.onBeforeCommandExecute` API to register a custom preprocessing listener.

When the command is executed, the logic inside the preprocessing listener will be executed.

```typescript
const univerAPI = FUniver.newAPI(univer);

univerAPI.onBeforeCommandExecute((command) => {
  const { id, type, params } = command;
  // Custom logic executed before the command is executed
})
```

If you want to prevent the command from executing before it is executed, you can `throw` an exception in the listener callback.

```typescript
const univerAPI = FUniver.newAPI(univer);

univerAPI.onBeforeCommandExecute((command) => {
  throw new Error('Editing is prohibited')
})
```

#### Listening Commands After Execution

After the command is executed, we can also pass a callback function to the `FUniver.onCommandExecuted` API to register a custom post-processing listener.

When the command is executed, the logic inside the post-processing listener will be executed.

```typescript
const univerAPI = FUniver.newAPI(univer);

univerAPI.onCommandExecuted((command) => {
  const { id, type, params } = command;
  // Custom logic executed after the command is executed
})
```

### Cancel Listening

The method that registers a command listener returns an `IDisposable` object, which can be destroyed by calling `IDisposable.dispose`.

It is recommended that you destroy listeners that are no longer in use to help improve the robustness of your program.

```javascript
const univerAPI = FUniver.newAPI(univer);

// Register a listener
const disposable = univerAPI.onBeforeCommandExecute((command) => {
  // custom preprocessing logic before the command is executed
})

// Example: Destroy the listener after 1 second
setTimeout(() => {
  // Destroy the listener
  disposable.dispose();
}, 1000);
```

### Execute Commands

If you already know the command ID and the parameters you need to pass, you can also execute the command using the `FUniver.executeCommand` method.

For example, we can set the value of cell A1 using the `sheet.command.set-range-values` command:

```typescript
const univerAPI = FUniver.newAPI(univer);

// Execute the command
univerAPI.executeCommand('sheet.command.set-range-values', {
  value: { v: "Hello, Univer!" },
  range: { startRow: 0, startColumn: 0, endRow: 0, endColumn: 0 }
});
```

## undo/redo

### undo

```ts
univerAPI.executeCommand('univer.command.undo')
```

### redo

```ts
univerAPI.executeCommand('univer.command.redo')
```

## UI

Please refer to the following documentation to extend the Univer UI :

- [Extending Canvas](/guides/customization/canvas/)
- [Custom Theme](/guides/customization/theme)

## WebSocket

Facade provides a convenient API `createSocket` for creating a WebSocket, simply by passing in a URL. You can then listen to open, message, close, and error events, and actively send messages with the send method and actively close with the close method.

```typescript
// Replace the URL with the address of your own WebSocket service
const ws = univerAPI.createSocket('ws://47.100.177.253:8449/ws')

ws.open$.subscribe(() => {
  console.log('websocket opened')
  ws.send('hello')
})

ws.message$.subscribe((message) => {
  console.log('websocket message', message)
  const content = JSON.parse(message.data).content
  if (!content.includes('command')) {
    return
  }

  const commandInfo = JSON.parse(content);
  const { command, options } = commandInfo;
  const { id, params } = command;

  // Upon receiving collaborative data, it is locally saved
  univerAPI.executeCommand(id, params, options)
});

ws.close$.subscribe(() => {
  console.log("websocket closed");
});

ws.error$.subscribe((error) => {
  console.log("websocket error", error);
});

univerAPI.onCommandExecuted((command, options) => {
  // Only synchronize local mutations
  if (command.type !== 2 || options?.fromCollab || options?.onlyLocal || command.id === 'doc.mutation.rich-text-editing') {
    return;
  }

  const commandInfo = JSON.stringify({ command, options: { fromCollab: true } })
  ws.send(commandInfo);
})
```

Note: Make sure there is a unitID when starting Univer. If the unitID is not specified, collaboration will not work.

## Register Formula

Using  Facade API, you can quickly and easily register custom formulas in the current Univer instance.

As shown in the following case, use `registerFunction` to register the algorithm, name, and description required by a `CUSTOMSUM` formula into the formula plugin at one time. After execution, the formula can be used. Enter `=CUSTOMSUM` in any blank cell to see the prompt.

```javascript
univerAPI.registerFunction({
  calculate: [
    [function (...variants) {
      let sum = 0;

      for(const variant of variants){
        sum += Number(variant) || 0;
      }

      return sum;
    }, 'CUSTOMSUM', 'Adds its arguments'],
    // ... more formulas
  ]
})
```

If you need to unregister the formula, you can call the `dispose` method.

```javascript
const functionDisposable = univerAPI.registerFunction({
   // calculate
})

// Unregister the function
functionDisposable.dispose();
```

If you want to provide more complete international content and description, you can also configure the `locales` and `description` fields. As follows.

```javascript
const FUNCTION_NAMES_USER = {
  USTOMSUM: 'CUSTOMSUM'
}

univerAPI.registerFunction({
  locales: {
    'zhCN': {
      formulaCustom: {
        CUSTOMSUM: {
          description: '将单个值、单元格引用或是区域相加，或者将三者的组合相加。',
          abstract: '求参数的和',
          links: [
            {
              title: '教学',
              url: 'https://support.microsoft.com/zh-cn/office/sum-%E5%87%BD%E6%95%B0-043e1c7d-7726-4e80-8f32-07b23e057f89',
            },
          ],
          functionParameter: {
            number1: {
              name: '数值1',
              detail: '要相加的第一个数字。 该数字可以是 4 之类的数字，B6 之类的单元格引用或 B2:B8 之类的单元格范围。',
            },
            number2: {
              name: '数值2',
              detail: '这是要相加的第二个数字。 可以按照这种方式最多指定 255 个数字。',
            },
          },
        },
        // ... more formulas
      },
    },
    'enUS': {
      formulaCustom: {
        CUSTOMSUM: {
          description: `You can add individual values, cell references or ranges or a mix of all three.`,
          abstract: `Adds its arguments`,
          links: [
            {
              title: 'Instruction',
              url: 'https://support.microsoft.com/en-us/office/sum-function-043e1c7d-7726-4e80-8f32-07b23e057f89',
            },
          ],
          functionParameter: {
            number1: {
              name: 'number1',
              detail: 'The first number you want to add. The number can be like 4, a cell reference like B6, or a cell range like B2:B8.',
            },
            number2: {
              name: 'number2',
              detail: 'This is the second number you want to add. You can specify up to 255 numbers in this way.',
            },
          },
        },
      }
    }
  },
  description: [
    {
      functionName: FUNCTION_NAMES_USER.CUSTOMSUM,
      aliasFunctionName: 'formulaCustom.CUSTOMSUM.aliasFunctionName',
      functionType: 15,
      description: 'formulaCustom.CUSTOMSUM.description',
      abstract: 'formulaCustom.CUSTOMSUM.abstract',
      functionParameter: [
        {
          name: 'formulaCustom.CUSTOMSUM.functionParameter.number1.name',
          detail: 'formulaCustom.CUSTOMSUM.functionParameter.number1.detail',
          example: 'A1:A20',
          require: 1,
          repeat: 0,
        },
        {
          name: 'formulaCustom.CUSTOMSUM.functionParameter.number2.name',
          detail: 'formulaCustom.CUSTOMSUM.functionParameter.number2.detail',
          example: 'B2:B10',
          require: 0,
          repeat: 1,
        },
      ],
    },
    // ... more formulas
  ],
  calculate: [
    [function (...variants) {
      let sum = 0;

      for (const variant of variants) {
        sum += Number(variant) || 0;
      }

      return sum;
    }, FUNCTION_NAMES_USER.CUSTOMSUM],
    // ... more formulas
  ]
})
```

Note

- Multiple languages can be set under `locales`. For naming rules, please refer to [LocaleType](/typedoc/@univerjs/core/enumerations/LocaleType). Translations for multiple formulas can be added under `functionList`. For detailed field descriptions, please refer to the [How to add formulas in Formula Engine](/guides/sheet/advanced/custom-formula/#how-to-add-formulas-in-formula-engine) section.
- `description` sets the description of the custom formula.
- `calculate` writes the specific algorithm and name mapping of the calculation formula. The input parameter is the content entered by the user when using the formula, which may be a number, a string, a Boolean value, or a array.

<Callout type="info" emoji="ℹ️">
  If you want to reuse the algorithm provided by the formula system and enhance the capability of the formula algorithm, you can register a custom formula through plug-in configuration. For detailed tutorials, please refer to [Custom Formula](/guides/sheet/advanced/custom-formula/).
</Callout>
